import { Client } from "./mod.ts";
import { User } from "./user.ts";
import { BranchAPI } from "./repo_branch.ts";
import { DeployKeyAPI } from "./repo_key.ts";
import { HookAPI } from "./repo_hook.ts";
import { CollaboratorAPI } from "./repo_collaborator.ts";
import { CommitAPI } from "./repo_commit.ts";
import { FileAPI } from "./repo_file.ts";

/**
 * Permission represents a API permission.
 */
export interface Permission {
  admin: boolean;
  push: boolean;
  pull: boolean;
}

/**
 * Repository represents a API repository.
 */
export interface Repository {
  id: number;
  owner: User;
  name: string;
  full_name: string;
  description: string;
  private: boolean;
  unlisted: boolean;
  fork: boolean;
  parent?: Repository;
  empty: boolean;
  mirror: boolean;
  size: number;
  html_url: string;
  ssh_url: string;
  clone_url: string;
  website: string;
  stars_count: number;
  forks_count: number;
  watchers_count: number;
  open_issues_count: number;
  default_branch: string;
  created_at: string;
  updated_at: string;
  permissions?: Permission;
}

/**
 * Options for creating a repository
 */
export interface CreateRepoOption {
  name: string;
  description?: string;
  private?: boolean;
  unlisted?: boolean;
  auto_init?: boolean;
  gitignores?: string;
  license?: string;
  readme?: string;
}

/**
 * Options for migrating a repository
 */
export interface MigrateRepoOption {
  clone_addr: string;
  auth_username?: string;
  auth_password?: string;
  uid: number;
  repo_name: string;
  mirror?: boolean;
  private?: boolean;
  unlisted?: boolean;
  description?: string;
}

/**
 * Options for editing issue tracker
 */
export interface EditIssueTrackerOption {
  enable_issues?: boolean;
  enable_external_tracker?: boolean;
  external_tracker_url?: string;
  tracker_url_format?: string;
  tracker_issue_style?: string;
}

/**
 * Options for editing wiki
 */
export interface EditWikiOption {
  enable_wiki?: boolean;
  allow_public_wiki?: boolean;
  enable_external_wiki?: boolean;
  external_wiki_url?: string;
}

/**
 * Options for forking a repository
 */
export interface ForkRepoOption {
  repo_name: string;
  description?: string;
  organization?: string;
}

/**
 * Repository API endpoints
 */
export class RepositoryAPI {
  // Sub APIs
  branches: BranchAPI;
  keys: DeployKeyAPI;
  hooks: HookAPI;
  collaborators: CollaboratorAPI;
  commits: CommitAPI;
  files: FileAPI;

  constructor(private client: Client) {
    // Initialize sub APIs
    this.branches = new BranchAPI(client);
    this.keys = new DeployKeyAPI(client);
    this.hooks = new HookAPI(client);
    this.collaborators = new CollaboratorAPI(client);
    this.commits = new CommitAPI(client);
    this.files = new FileAPI(client);
  }

  /**
   * List repositories for the authenticated user
   */
  async listMyRepos(): Promise<Repository[]> {
    return await this.client.doRequest<Repository[]>("GET", "/user/repos");
  }

  /**
   * List repositories for a user
   */
  async listUserRepos(username: string): Promise<Repository[]> {
    return await this.client.doRequest<Repository[]>("GET", `/users/${username}/repos`);
  }

  /**
   * List repositories for an organization
   */
  async listOrgRepos(org: string): Promise<Repository[]> {
    return await this.client.doRequest<Repository[]>("GET", `/orgs/${org}/repos`);
  }

  /**
   * Create a repository for authenticated user
   */
  async createRepo(options: CreateRepoOption): Promise<Repository> {
    return await this.client.doRequest<Repository>("POST", "/user/repos", {
      "Content-Type": "application/json",
    }, options);
  }

  /**
   * Create an organization repository for authenticated user
   */
  async createOrgRepo(org: string, options: CreateRepoOption): Promise<Repository> {
    return await this.client.doRequest<Repository>("POST", `/org/${org}/repos`, {
      "Content-Type": "application/json",
    }, options);
  }

  /**
   * Get repository information
   */
  async getRepo(owner: string, repoName: string): Promise<Repository> {
    return await this.client.doRequest<Repository>("GET", `/repos/${owner}/${repoName}`);
  }

  /**
   * Delete a repository
   */
  async deleteRepo(owner: string, repoName: string): Promise<void> {
    await this.client.doRequest<void>("DELETE", `/repos/${owner}/${repoName}`);
  }

  /**
   * Migrate a repository from other Git hosting sources
   */
  async migrateRepo(options: MigrateRepoOption): Promise<Repository> {
    return await this.client.doRequest<Repository>("POST", "/repos/migrate", {
      "Content-Type": "application/json",
    }, options);
  }

  /**
   * Edit issue tracker options
   */
  async editIssueTracker(owner: string, repoName: string, options: EditIssueTrackerOption): Promise<void> {
    await this.client.doRequest<void>("PATCH", `/repos/${owner}/${repoName}/issue-tracker`, {
      "Content-Type": "application/json",
    }, options);
  }

  /**
   * Edit wiki options
   */
  async editWiki(owner: string, repoName: string, options: EditWikiOption): Promise<void> {
    await this.client.doRequest<void>("PATCH", `/repos/${owner}/${repoName}/wiki`, {
      "Content-Type": "application/json",
    }, options);
  }

  /**
   * Sync a mirror repository
   */
  async mirrorSync(owner: string, repoName: string): Promise<void> {
    await this.client.doRequest<void>("POST", `/repos/${owner}/${repoName}/mirror-sync`, {
      "Content-Type": "application/json",
    });
  }

  /**
   * List repository forks
   */
  async listForks(owner: string, repoName: string): Promise<Repository[]> {
    return await this.client.doRequest<Repository[]>("GET", `/repos/${owner}/${repoName}/forks`);
  }

  /**
   * Fork a repository
   */
  async forkRepo(owner: string, repoName: string, options: ForkRepoOption): Promise<Repository> {
    return await this.client.doRequest<Repository>("POST", `/repos/${owner}/${repoName}/fork`, {
      "Content-Type": "application/json",
    }, options);
  }
}